#include "game_server.hpp"
#include "protocol_dist.hpp"
#include "room.hpp"
#include <iostream>
using namespace std;
using namespace winux;

std::map<winux::String, winux::SharedPointer<winux::DllLoader>> dllMap;

std::string camelToSnake(const std::string &camelCase)
{
	std::string snakeCase;
	for (size_t i = 0; i < camelCase.length(); ++i)
	{
		if (isupper(camelCase[i]) && i > 0)
		{
			snakeCase += '_';
		}
		snakeCase += tolower(camelCase[i]);
	}
	return snakeCase;
}

void GameServer::run()
{
#ifdef _WIN32
	// ����WINDOWS�µĲ���
	ConfigureSettings settings("ws_server_win.settings");
#else
	ConfigureSettings settings("configs/ws_server.settings");
#endif
	ws::WsHttpServer server(settings);
	server.onOpenHandler([&](pWsReqCtx requestCtxPtr, http::Url const &url)
						 { this->_onOpen(requestCtxPtr, url); });
	server.onMessageHandler([&](pWsReqCtx requestCtxPtr, http::Url const &url, winux::AnsiString const &message, int messageType)
							{ this->_onMessage(requestCtxPtr, url, message, messageType); });
	server.onCloseHandler([&](pWsReqCtx requestCtxPtr, http::Url const &url, winux::uint16 errCode, winux::AnsiString const &errStr)
						  { this->_onClose(requestCtxPtr, url, errCode, errStr); });
	server.onErrorHandler([&](pWsReqCtx requestCtxPtr, http::Url const &url, ws::ErrorCode errCode)
						  { this->_onError(requestCtxPtr, url, errCode); });

	server.run(nullptr);
}

User *GameServer::newUser()
{
	__mtxForUserList.lock();
    userList.push_back(SharedPointer<User>(new User()));
    User *user = userList[userList.size() - 1].get();
	__mtxForUserList.unlock();
    return user;
}

GameServer::GameServer() : __mtxForUserList(true), __mtxForRoomList(true)
{
}

void GameServer::runAction(Protocol *msg, pWsReqCtx ctx)
{
	String actionName = camelToSnake(msg->getClassName().substr(3));
	cout << "action:" << actionName << endl;

#ifdef _WIN32
	// ����WINDOWS�µĲ���
	String path = actionName + ".dll";
#else
	String path = "actions/" + actionName + ".mdo";
#endif
	if (!dllMap[path])
	{
		dllMap[path].attachNew(new winux::DllLoader(path));
	}
	winux::DllLoader *dll = dllMap[path].get();
	auto func = dll->func<ActionType>("ActionMain");
	if (func)
	{
		func.call(msg, ctx, *this);
	}
	else
	{
		cout << winux::LocalFromUtf8(dll->errStr()) << endl;
	}
}

Room *GameServer::newRoom()
{
	__mtxForRoomList.lock();
	SharedPointer<Room> p(new Room());
	roomList.push_back(p);
	auto room = roomList[roomList.size() - 1].get();
	roomIdCounter++;
	room->id = roomIdCounter;
	__mtxForRoomList.unlock();
	return room;
}

Room *GameServer::getRoom(int id)
{
	__mtxForRoomList.lock();
	for (auto room : roomList)
	{
		if (room->id == id)
		{
			__mtxForRoomList.unlock();
			return room.get();
		}
	}
	__mtxForRoomList.unlock();
	return nullptr;
}

Room *GameServer::getRoomByUser(User *user)
{
	__mtxForRoomList.lock();
	for (auto room : roomList)
	{
		if (room->hasUser(user))
		{
			__mtxForRoomList.unlock();
			return room.get();
		}
	}
	__mtxForRoomList.unlock();
	return nullptr;
}

User *GameServer::getUser(pWsReqCtx ctx)
{
	__mtxForUserList.lock();
	for (auto iter = userList.begin(); iter != userList.end(); iter++)
	{
		if ((*iter)->isYourCtx(ctx))
		{
			__mtxForUserList.unlock();
			return (*iter).get();
		}
	}
	__mtxForUserList.unlock();
	return nullptr;
}
void GameServer::_onOpen(pWsReqCtx requestCtxPtr, http::Url const &url)
{
	cout << "open:" << url.toString() << endl;
}

void GameServer::_onMessage(pWsReqCtx requestCtxPtr, http::Url const &url, winux::AnsiString const &message, int messageType)
{
	auto pMsg = ProtocolFactory::CreateFromMixed(Mixed().json(message));
	cout << "message: " << pMsg->toMixed().json() << endl;
	runAction(pMsg.get(), requestCtxPtr);
}

void GameServer::_onClose(pWsReqCtx requestCtxPtr, http::Url const &url, winux::uint16 errCode, winux::AnsiString const &errStr)
{
	__mtxForUserList.lock();
	for (auto user : userList)
	{
		if (!user->isLogged)
		{
			continue;
		}
		if (user->isYourCtx(requestCtxPtr))
		{
			_userDisconnect(user.get());
			break;
		}
	}
	__mtxForUserList.unlock();
	cout << "closed" << endl;
}

void GameServer::_onError(pWsReqCtx requestCtxPtr, http::Url const &url, ws::ErrorCode errCode)
{
	__mtxForUserList.lock();
	for (auto user : userList)
	{
		if (!user->isLogged)
		{
			continue;
		}
		if (user->isYourCtx(requestCtxPtr))
		{
			_userDisconnect(user.get());
			break;
		}
	}
	__mtxForUserList.unlock();
	cout << "error:" << errCode << endl;
}

void GameServer::_userDisconnect(User *user)
{
	// leaveRoom
	auto room = getRoomByUser(user);
	if (room)
	{
		room->userLeave(user);
	}
	// logout
	user->logout();
}